package com.swiftride.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.swiftride.constants.Constants;
import com.swiftride.constants.GlobalExceptionCode;
import com.swiftride.exception.GlobalException;
import com.swiftride.feign.UaaFeign;
import com.swiftride.mapper.DriverMapper;
import com.swiftride.pojo.domain.Driver;
import com.swiftride.pojo.domain.DriverSetting;
import com.swiftride.pojo.domain.DriverSummary;
import com.swiftride.pojo.domain.DriverWallet;
import com.swiftride.pojo.dto.DriverLocation2GeoDto;
import com.swiftride.pojo.dto.DriverSettingDto;
import com.swiftride.pojo.param.RegisterParam;
import com.swiftride.pojo.properties.DriveSettingProperties;
import com.swiftride.pojo.result.DriverTodayInfoResult;
import com.swiftride.pojo.vo.UnsignedDriverVo;
import com.swiftride.result.R;
import com.swiftride.service.IDriverService;
import com.swiftride.service.IDriverSettingService;
import com.swiftride.service.IDriverSummaryService;
import com.swiftride.service.IDriverWalletService;
import com.swiftride.template.AppWeChatTemplate;
import com.swiftride.utils.AssertUtil;
import com.swiftride.utils.BitStatesUtil;
import com.swiftride.utils.SMSUtils;
import com.swiftride.utils.ValidateCodeUtils;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;


/**
 * <p>
 * 司机对象 服务实现类
 * </p>
 *
 * @author wy
 * @since 2024-07-27
 */
@Service
@Slf4j
public class DriverServiceImpl extends ServiceImpl<DriverMapper, Driver> implements IDriverService {

    //属性
    @Autowired
    private DriveSettingProperties driveSettingProperties;

    //service
    @Autowired
    private IDriverWalletService walletService;

    @Autowired
    private IDriverSummaryService summaryService;

    @Autowired
    private IDriverSettingService settingService;

    @Autowired
    private DriverMapper driverMapper;

    //组件
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private AppWeChatTemplate appWeChatTemplate;

    //feign
    @Autowired
    private UaaFeign uaaFeign;

    @Override
    @GlobalTransactional(name = "driver-wechat-register", rollbackFor = Exception.class)
    public void register(String openidCode, String phoneCode) {
        //1.参数效验 使用jsr303完成
        //2.业务验证
        //2.1获取openid
        String openid = appWeChatTemplate.getOpenid(openidCode);
        //2.2判断司机是否已经注册过
        Driver driver = super.getOne(new LambdaQueryWrapper<Driver>().eq(Driver::getOpenId, openid));
        AssertUtil.isNull(driver, GlobalExceptionCode.PARAM_PHONE_EXIST);
        //2.3获取电话号码
        String phoneNum;
        if (StrUtil.isBlank(phoneCode)) {
            phoneNum = Constants.DEFAULT_PHONE;
        } else {
            phoneNum = appWeChatTemplate.getPhoneNum(phoneCode);
        }
        //3.业务实现
        //3.1设置初始密码
        String password = Constants.DEFAULT_PASSWORD;
        //3.2使用BCrypt加密
        String pwHash = BCrypt.hashpw(password, BCrypt.gensalt());
        //3.3注册司机的登录信息并返回登录id
        RegisterParam registerParam = new RegisterParam();
        registerParam.init();
        registerParam.setUsername(phoneNum);
        registerParam.setPassword(password);
        registerParam.setType(Constants.Login.TYPE_DRIVER);
        registerParam.setOpenId(openid);
        registerParam.setPhone(phoneNum);
        //调用feign接口进行注册,需要满足分布式事务
        R<Long> loginR = uaaFeign.register(registerParam);
        AssertUtil.isTrue(loginR.isSuccess(), GlobalExceptionCode.SERVICE_REQUEST_ERROR);
        Long loginId = loginR.getData();
        //3.4初始化司机信息
        driver = new Driver();
        driver.init();
        driver.setId(loginId);
        driver.setOpenId(openid);
        driver.setPhone(phoneNum);
        driver.setBitState(BitStatesUtil.addState(driver.getBitState(), BitStatesUtil.OP_PHONE));
        //3.5保存司机表
        save(driver);
        //3.5保存司机关联表
        initDriverAssociation(driver.getId(), pwHash);
    }

    @Override
    public void driverOnline() {
        //2.业务校验
        long loginId = StpUtil.getLoginIdAsLong();
        DriverSetting setting = settingService.getById(loginId);
        AssertUtil.isNotNull(setting, GlobalExceptionCode.SERVICE_ERROR);
        //3.业务实现
        String key = String.format(Constants.Redis.DRIVER_ONLINE_KEY, loginId);
        DriverSettingDto driverSettingDto = BeanUtil.copyProperties(setting, DriverSettingDto.class);
        redisTemplate.opsForValue().set(key, driverSettingDto, 10, TimeUnit.MINUTES);
    }

    @Override
    public void driverOffline() {
        String loginIdStr = StpUtil.getLoginIdAsString();
        String key = String.format(Constants.Redis.DRIVER_ONLINE_KEY, loginIdStr);
        //删除redis中的司机上线状态
        redisTemplate.delete(key);
        //删除redisGEO中的司机位置
        redisTemplate.opsForGeo().remove(Constants.Redis.DRIVER_LOCATION_KEY, loginIdStr);
    }

    @Override
    public void driverLocation2Geo(DriverLocation2GeoDto driverLocation2GeoDto) {
        //2.业务校验--司机必须处于上线状态
        String loginIdStr = StpUtil.getLoginIdAsString();
        String onlineKey = String.format(Constants.Redis.DRIVER_ONLINE_KEY, loginIdStr);
        Object setting = redisTemplate.opsForValue().get(onlineKey);
        AssertUtil.isNotNull(setting, GlobalExceptionCode.SERVICE_ERROR);
        //3.业务实现
        //发送GEO心跳,说明司机处于上线状态,更新redis中上线时间
        redisTemplate.opsForValue().set(onlineKey, setting, 10, TimeUnit.HOURS);
        //更新司机位置到GEO
        Point point = new Point(driverLocation2GeoDto.getLongitude(), driverLocation2GeoDto.getLatitude());
        redisTemplate.opsForGeo().add(Constants.Redis.DRIVER_LOCATION_KEY, point, loginIdStr);
    }

    @Override
    public List<UnsignedDriverVo> getUnsignedContractList() {
        List<UnsignedDriverVo> list = driverMapper.getUnSignedContractList();
        list = list.stream().filter(driver -> {
            Long bitState = driver.getBitState();
            return !BitStatesUtil.hasState(bitState, BitStatesUtil.OP_DRIVER_AGREEMENT);
        }).toList();
        return list;
    }

    @Override
    public DriverTodayInfoResult getDriverTodayInfoDto() {
        long driverId = StpUtil.getLoginIdAsLong();
        DriverTodayInfoResult driverTodayInfoResult = driverMapper.getDriverTodayInfoDto(driverId);
        return driverTodayInfoResult;
    }

    private void initDriverAssociation(Long id, String password) {
        //初始化关联表setting
        DriverSetting driverSetting = new DriverSetting();
        driverSetting.init();
        driverSetting.setId(id);
        driverSetting.setOrderDistance(driveSettingProperties.getOrderDistance());
        driverSetting.setRangeDistance(driveSettingProperties.getRangeDistance());
        settingService.save(driverSetting);

        //初始化关联表summary
        DriverSummary driverSummary = new DriverSummary();
        driverSummary.init();
        driverSummary.setId(id);
        summaryService.save(driverSummary);

        //初始化关联表wallet
        DriverWallet driverWallet = new DriverWallet();
        driverWallet.init();
        driverWallet.setId(id);
        driverWallet.setPassword(password);
        walletService.save(driverWallet);
    }

    /**
     * 根据手机号发送短信
     *
     * @param phoneNum 手机号
     */
    private void sendTextMessage(String phoneNum) {
        //生成 随机的4位验证码
        String code = ValidateCodeUtils.generateValidateCode(4).toString();
        //调用阿里云提供的短信服务API 完成发送短信
        //参数分别是signName, templateCode, phoneNumbers, param
        try {
            SMSUtils.sendMessage("阿里云短信测试", phoneNum, code);
        } catch (Exception e) {
            e.printStackTrace();
            throw new GlobalException(GlobalExceptionCode.SERVICE_ERROR);
        }
    }
}
